package ro.qi.framework.sys.db.util;

import java.math.BigDecimal;
import java.sql.CallableStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Types;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;

/**
 * This class represents a Row in a database query result. It contains
 * a collection of Column objects. A Vector is used to hold the
 * Column objects to preserve the column order in the base ResultSet.
 *
 * @author
 */
public class Row
{
	private Column[] columns; //Columns corresponding to each row


	/**
	 * Constructor. Reads the columns from the current row in the
	 * specified ResultSet and creates the corresponding ColumnValue
	 * objects.
	 *
	 * @param rs the ResultSet
	 * @exception SQLException, if thrown by any JDBC API call
	 */
	public Row(ResultSet rs) throws SQLException, UnsupportedTypeException
	{


		ResultSetMetaData rsmd = rs.getMetaData();
	    int cols = rsmd.getColumnCount();
	    columns = new Column[cols];

	    // Columns are numbered from 1 in the ResultSet
	    for (int i = 1; i <= cols; i++)
	    {
	        int type = rsmd.getColumnType(i);
	        switch (type) {
	            case Types.DATE:
	                columns[i - 1] = new Column(rsmd.getColumnName(i),
	                    rs.getDate(i));
	                break;
	            case Types.TIME:
	                columns[i - 1] = new Column(rsmd.getColumnName(i),
	                    rs.getTime(i));
	                break;
	            case Types.TIMESTAMP:
	                columns[i - 1] = new Column(rsmd.getColumnName(i),
	                    rs.getTimestamp(i));
	                break;
	            case Types.REAL:
	            case Types.FLOAT:
	                columns[i - 1] = new Column(rsmd.getColumnName(i),
	                    new Float(rs.getFloat(i)));
	                break;
	            case Types.DOUBLE:
	                columns[i - 1] = new Column(rsmd.getColumnName(i),
	                    new Double(rs.getDouble(i)));
	                break;
	            case Types.TINYINT:
	                columns[i - 1] = new Column(rsmd.getColumnName(i),
	                    new Byte(rs.getByte(i)));
	                break;
	            case Types.SMALLINT:
	                columns[i - 1] = new Column(rsmd.getColumnName(i),
	                    new Short(rs.getShort(i)));
	                break;
	            case Types.INTEGER:
	                columns[i - 1] = new Column(rsmd.getColumnName(i),
	                    new Integer(rs.getInt(i)));
	                break;
	            case Types.BIGINT:
	                columns[i - 1] = new Column(rsmd.getColumnName(i),
	                    new Long(rs.getLong(i)));
	                break;
	            case Types.DECIMAL:
	            case Types.NUMERIC:
	                columns[i - 1] = new Column(rsmd.getColumnName(i),
	                     rs.getBigDecimal(i) == null? new BigDecimal(0):rs.getBigDecimal(i));
	                break;
	            case Types.CHAR:
	            case Types.LONGVARCHAR:
	            case Types.VARCHAR:
	                columns[i - 1] = new Column(rsmd.getColumnName(i),
	                    rs.getString(i));
	                break;
	            default:
	                throw new UnsupportedTypeException("Unsupported SQL " +
	                    "data type: " + type);
	        }
	    }
	}


	/**
	 * Constructor. Reads the output parameters of the executed
	 * Callable Statement
	 *
	 * @param CallableStatement The executed statement from which
	 * 							the output params are read
	 * @param HashMap			Map containing the position & Type
	 * 							of the output parameters so that they
	 * 							can be accordingly read from the
	 * 							executed statement
	 *
	 * @exception SQLException, if thrown by any JDBC API call
	 */
	public Row(CallableStatement pStmt, HashMap pOutputParamMapping) throws SQLException, UnsupportedTypeException
	{		
		int max = 0;
	    Set _set = pOutputParamMapping.keySet();
		
		/* find maximum column index */
		Integer[] vals = (Integer[])_set.toArray(new Integer[0]);
		for( int i = 0 ; i < vals.length ; i++ ) {
			if( max < vals[i].intValue() )
				max = vals[i].intValue();
		}
		columns = new Column[max];

		Iterator _keyIter = _set.iterator();
		Integer _position;
		String _type = null;
		int _index = pOutputParamMapping.size() -1;
		ResultSetMetaData meta = pStmt.getMetaData();
		while(_keyIter.hasNext())
		{
			_position = (Integer) _keyIter.next();
			_type     = (String) pOutputParamMapping.get(_position);

			if (_type.equals(DBConnectionUtil.BIGDECIMAL_TYPE))
	       	{
	     	   columns[_position.intValue() - 1] = new Column("column"+_index,
	     	   								 pStmt.getBigDecimal(_position.intValue()));
	        }
	        else if (_type.equals(DBConnectionUtil.BYTE_TYPE))
	        {
	       		columns[_position.intValue() - 1] = new Column("column"+_index,
	       									 new Byte(pStmt.getByte(_position.intValue())));
	        }
	        else if (_type.equals(DBConnectionUtil.DATE_TYPE))
	        {
	        	columns[_position.intValue() - 1] = new Column("column"+_index,
	     	   								 pStmt.getDate(_position.intValue()));
	        }
	        else if (_type.equals(DBConnectionUtil.DOUBLE_TYPE))
	        {
	        	columns[_position.intValue() - 1] = new Column("column"+_index,
	     	   								 new Double(pStmt.getDouble(_position.intValue())));
	        }
	        else if (_type.equals(DBConnectionUtil.FLOAT_TYPE))
	        {
	        	columns[_position.intValue() - 1] = new Column("column"+_index,
	     	   								 new Float(pStmt.getFloat(_position.intValue())));
	        }
	        else if (_type.equals(DBConnectionUtil.INT_TYPE))
	        {
	        	columns[_position.intValue() - 1] = new Column("column"+_index,
	     	   								 new Integer(pStmt.getInt(_position.intValue())));
	        }
	        else if (_type.equals(DBConnectionUtil.LONG_TYPE))
	        {
	        	columns[_position.intValue() - 1] = new Column("column"+_index,
	     	   								 new Long(pStmt.getLong(_position.intValue())));
	        }
	        else if (_type.equals(DBConnectionUtil.SHORT_TYPE))
	        {
	        	columns[_position.intValue() - 1] = new Column("column"+_index,
	     	   								 new Short(pStmt.getShort(_position.intValue())));
	        }
	        else if (_type.equals(DBConnectionUtil.STRING_TYPE))
	        {
	        	columns[_position.intValue() - 1] = new Column("column"+_index,
	     	   								 pStmt.getString(_position.intValue()));
	        }
	        else if (_type.equals(DBConnectionUtil.TIME_TYPE))
	        {
	        	columns[_position.intValue() - 1] = new Column("column"+_index,
	     	   								 pStmt.getTime(_position.intValue()));
	        }
			else if (_type.equals(DBConnectionUtil.TIMESTAMP_TYPE))
			{
				columns[_position.intValue() - 1] = new Column("column"+_index,
											 pStmt.getTimestamp(_position.intValue()));
			}
			else if (_type.equals(DBConnectionUtil.ORACLE_CURSOR_TYPE))
			{
				columns[_position.intValue() - 1] = new Column("column"+_index,
											 pStmt.getObject(_position.intValue()));
			}
   	        /*else if (_className.endsWith("Boolean"))
	        {
      		pstmt.registerOutParameter(pIndex, Types.
	        }*/
	                /*else if (_className instanceof Bytes) {
	         pstmt.setBytes(i + 1, v.getBytes());
	         }*/
	        else
	        {
	        	throw new UnsupportedTypeException("Unsupported SQL " +
	                    "data type: " + _type);
	        }
	        _index--;
	    }
    }



   /**
    * Returns the number of columns in this row.
    */
    public int getColumnCount()
	{
        return columns.length;
    }

    /**
     * Returns an array of Columns in this row.
     */
    public Column[] getColumns()
    {
        return columns;
    }

    /**
     * Returns the column in the row corresponding to the
     * given index. An index value of 0 returns the first
     * column
     *
     * @throws NoSuchColumnException
     */
	public Column getColumn(int pIndex) throws NoSuchColumnException
	{
		if (pIndex >= columns.length || pIndex < 0)
		{
			throw new NoSuchColumnException("Result Set does not contain any column at index : " + pIndex);
		}

		return columns[pIndex];
	}

	/**
	 * Gets the value of the field at the specified index
	 *
     * @throws NoSuchColumnException
	 */
	public Object getValue(int pColumnNo) throws NoSuchColumnException
	{
		Column _col = getColumn(pColumnNo-1); //column# start with 0
		return _col.getValue();
	}

	/**
	 * Gets the value of the given field name
	 *
	 * @throws NoSuchColumnException
	 */
	public Object getValue(String pColName) throws NoSuchColumnException
	{
		for(int _col=0; _col < columns.length; _col++)
		{
			if(getColumn(_col).getName().equalsIgnoreCase(pColName))
			{
				return getColumn(_col).getValue();
			}
		}

		throw new NoSuchColumnException("Result Set does not contain any column with Name : " + pColName);
	}

	public String toString()
	{
		String _str = "";
		if (columns != null)
		{
			_str+= "\nRow:\n";
			for (int i=0; i< columns.length ; i++)
			{
				_str+= "\n" + columns[i].toString();
			}
		}
		return _str;
	}
}

